package com.spring;

import com.sun.org.apache.bcel.internal.generic.RET;
import com.wyt.service.WytBeanPostProcessor;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

//spring 容器
public class WytApplicationContext {

    private Class configClass;
    //单例池
    private ConcurrentHashMap<String,Object> singletonObject = new ConcurrentHashMap<>();
    //bean定义
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    //BeanPostProcessor的容器
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public WytApplicationContext(Class configClass) throws Exception {
        this.configClass = configClass;
        String path = this.getPath(configClass);
        this.scanning(path);
        establish();
    }

    //获取扫描路径
    private String getPath(Class configClass){
        //解析配置类,将注解解析-->拿到扫描路径-->扫描
        Annotation declaredAnnotation = configClass.getDeclaredAnnotation(ComponentScan.class);
        if(declaredAnnotation==null){
            throw new RuntimeException("没有找到ComponentScan");
        }
        ComponentScan componentScan = (ComponentScan) declaredAnnotation;
        String path = componentScan.value();
        System.out.println(path);
        return path;
    }

    //扫描路径下的类
    private void scanning(String path) throws Exception {
        //三种类加载
        /***
         * BootStrap-->jre/lib
         * Ext -->jre/ext/lib
         * App -->classPath
         */
        //应用类加载器
        ClassLoader classLoader = WytApplicationContext.class.getClassLoader();
        //路径转换
        path  = path.replace(".", "/");
        URL resource = classLoader.getResource(path);
        //通过路径获取文件
        File file = new File(resource.getFile());
        if(file.isDirectory()){
            //判断如果是目录,拿文件
            File[] files = file.listFiles();
            for (File f:files) {
                //获取路径
                String fileName = f.getAbsolutePath();
                if(!fileName.endsWith(".class")){
                    this.scanning(fileName);
                }
                String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"))
                        .replace("\\",".");
                Class<?> aClass = classLoader.loadClass(className);
                if(aClass.isAnnotationPresent(Component.class)){
                    if(BeanPostProcessor.class.isAssignableFrom(aClass)){
                        //判断是不是BeanPostProcessor,是的话直接拿
                        BeanPostProcessor beanPostProcessor = (BeanPostProcessor) aClass.getDeclaredConstructor().newInstance();
                        beanPostProcessorList.add(beanPostProcessor);
                    }
                    //如果有Component注解,当前是一个bean,我们处理下
                    Component annotation = aClass.getAnnotation(Component.class);
                    String beanName = annotation.value();
                    //通过class 我们判断下是单例bean 还是原型
                    //BeanDefinition
                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setClazz(aClass);
                    if (aClass.isAnnotationPresent(Scope.class)){
                        //存在作用域
                        Scope declaredAnnotation = aClass.getDeclaredAnnotation(Scope.class);
                        beanDefinition.setScope(declaredAnnotation.value());
                    }else{
                        //Scope没有默认值 默认单例
                        beanDefinition.setScope(BeanDefinition.ff.singleton);
                    }
                    beanDefinitionMap.put(beanName,beanDefinition);
                }
            }
        }
    }

    //填充单例池
    private void establish() throws Exception {
        for (String beanName:beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if(Objects.equals(beanDefinition.getScope(),BeanDefinition.ff.singleton)){
                //单例 创建,然后放单例池
                Object bean = creatBean(beanName,beanDefinition);
                singletonObject.put(beanName,bean);
            }
        }
    }

    //创建
    private Object creatBean(String beanName,BeanDefinition beanDefinition) throws Exception {
        Class clazz = beanDefinition.getClazz();
        Object bean = clazz.getDeclaredConstructor().newInstance();
        //对属性进行赋值,依赖注入.
        for (Field declaredField:clazz.getDeclaredFields()) {
            //加了autowired注解
            if(declaredField.isAnnotationPresent(Autowired.class)){
                //根据名字.
                Object beanAutowired = getBean(declaredField.getName());
                //防止修饰符是私有的
                declaredField.setAccessible(true);
                declaredField.set(bean,beanAutowired);
            }
        }
        //aware回调
        if(bean instanceof BeanNameAware){
            ((BeanNameAware)bean).setBeanName(beanName);
        }

        //调用BeanPostProcessor before
        for(BeanPostProcessor beanPostProcessor:beanPostProcessorList){
            bean = beanPostProcessor.postProcessorBeforeInitialization(bean,beanName);
        }

        //初始化
        if(bean instanceof InitializingBean){
            ((InitializingBean)bean).afterPropertiesSet();
        }

        //调用BeanPostProcessor after
        for(BeanPostProcessor beanPostProcessor:beanPostProcessorList){
            bean = beanPostProcessor.postProcessorAfterInitialization(bean,beanName);
        }

        return bean;
    }

    public Object getBean(String beanName) throws Exception{
        //判断下我们有没有这个定义
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition==null){
            throw new RuntimeException("该bean不存在");
        }
        String scope = beanDefinition.getScope();
        Object bean;
        if(Objects.equals(scope,BeanDefinition.ff.singleton)){
            //表示为单例
            bean = singletonObject.get(beanName);
        }else{
            //表示是个原型bean
            bean = creatBean(beanName,beanDefinition);
        }
        return bean;
    }


}
